import math.big

struct IntegerRadix {
	digit_string string
	radix        u32
}

type TestInteger = IntegerRadix | []u8 | big.Integer | i64 | int | string | u32 | u64

fn (a TestInteger) parse() big.Integer {
	return match a {
		big.Integer {
			a
		}
		string {
			big.integer_from_string(a) or { panic('Cannot read decimal') }
		}
		int {
			big.integer_from_int(a)
		}
		u32 {
			big.integer_from_u32(a)
		}
		u64 {
			big.integer_from_u64(a)
		}
		i64 {
			big.integer_from_i64(a)
		}
		[]u8 {
			big.integer_from_bytes(a)
		}
		IntegerRadix {
			big.integer_from_radix(a.digit_string, a.radix) or {
				panic('Cannot read decimal base ${a.radix}')
			}
		}
	}
}

struct IntegerFromTest {
	value    TestInteger
	expected string // prefix with 0x for hex
}

// vfmt off
const integer_from_int_test_data = [
	// use int
	IntegerFromTest{ 0, '0' },
	IntegerFromTest{ 1, '1' },
	IntegerFromTest{ 255, '255' },
	IntegerFromTest{ 127, '127' },
	IntegerFromTest{ 1024, '1024' },
	IntegerFromTest{ 2147483647, '0x7fffffff' },
]

const integer_from_u64_test_data = [
	// use u64
	IntegerFromTest{ u64(0), '0' },
	IntegerFromTest{ u64(1), '1' },
	IntegerFromTest{ u64(255), '255' },
	IntegerFromTest{ u64(127), '127' },
	IntegerFromTest{ u64(1024), '1024' },
	IntegerFromTest{ u64(4294967295), '0xffffffff' },
	IntegerFromTest{ u64(4398046511104), '0x40000000000' },
	IntegerFromTest{ u64(-1), '0xffffffffffffffff' },
]

const integer_from_bytes_test_data = [
	// use []u8
	IntegerFromTest{ []u8{}, '0' },
	IntegerFromTest{ [u8(0)], '0' },
	IntegerFromTest{ [u8(0x13), 0x37], '0x1337' },
	IntegerFromTest{ [u8(0x13), 0x37, 0xca], '0x1337ca' },
	IntegerFromTest{ [u8(0x13), 0x37, 0xca, 0xfe], '0x1337cafe' },
	IntegerFromTest{ [u8(0x13), 0x37, 0xca, 0xfe, 0xba], '0x1337cafeba' },
	IntegerFromTest{ [u8(0x13), 0x37, 0xca, 0xfe, 0xba, 0xbe], '0x1337cafebabe' },
]

const integer_from_string_test_data = [
	// use string
	IntegerFromTest{ '00000000', '0' },
	IntegerFromTest{ '00', '0' },
	IntegerFromTest{ '0', '0' },
	IntegerFromTest{ '1', '1' },
	IntegerFromTest{ '0012', '12' },
	IntegerFromTest{ '1349173614', '1349173614' },
	IntegerFromTest{ '+24', '24' },
	IntegerFromTest{ '-325', '-325' },
	IntegerFromTest{ '-2147483648', '-2147483648' },
	IntegerFromTest{ '2147483647', '2147483647' },
]

const integer_from_radix_test_data = [
	// use IntegerRadix
	IntegerFromTest{ IntegerRadix{ '101010', 2 }, '42' },
	IntegerFromTest{ IntegerRadix{ '1010', 2 }, '10' },
	IntegerFromTest{ IntegerRadix{ '-0000101', 2 }, '-5' },
	IntegerFromTest{ IntegerRadix{ 'CAFE', 16 }, '0xcafe' },
	IntegerFromTest{ IntegerRadix{ 'DED', 16 }, '0xded' },
	IntegerFromTest{ IntegerRadix{ '-abcd', 16 }, '-43981' },
]
// vfmt on

// is_*
struct IsXTest {
	value    TestInteger
	expected bool
}

// vfmt off
const is_power_of_2_test_data = [
	IsXTest{ u32(0b110000000000), false },
	IsXTest{ "537502395172353242345", false },
	IsXTest{ "590295810358705700000", false },
	IsXTest{ "1125899906842624", true },
	IsXTest{ "590295810358705651712", true },
	IsXTest{ "4611686018427388002", false },
	IsXTest{ "31195165372897259196222538898096203590151924108450147950531565441852619837316692843188389598728651769482088968838700984268947453885587967878549286444999755742573423371025356539077075265986419171772426279084559025861175301940492273427120221755816136975739916983004778387946699939545354293487098252428954036286183995782377175227121587657233553706589448547148066273280603243167958729707736664649187444136702017299877489729451997277875868782399735511520086969969766278182145454186690598629675562422923132555707758646587702550600894625696538109646366308973392363200122154242784576162149305816215109893613161331026672647000825615987247035266514313689413563779184515427920269935280569035788081552413007563772309295149800172031645681720569680154349893907395864528243629654386620034655445226295834594630792819545156798270599481573436039129275439653984521135652249263653985326577886990615665734998585216581730937090703518997669223802429711292740491797911117308280939507973715877108492303860661291987529284719391551256912380499409630332506454532263266457209921483705507359152839264852808182519011100934922492651373859423833024010283468753147686188675294998119637462200763443029190704825719342806119404339670408160210011918981038977425180213726646978883378058838510330816291941879581568740273684084511318422175006728346276489384220596694727036836687670632486602655240593463885077059375085482211864761344849868123074687509143827139683659102930877963676911995751113159944160296419825178911962487549670296207457410515598040046860567719116506974858703739531721991704589155513182996455827177472", true },
]

const is_odd_test_data = [
	IsXTest{ u32(0), false },
	IsXTest{ u32(1), true },
	IsXTest{ u32(1805), true },
	IsXTest{ "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", true },
	IsXTest{ "57495732561923751347562394571325712358054", false },
]
// vfmt on

struct AddTest {
	augend TestInteger
	addend TestInteger
	sum    TestInteger
}

// vfmt off
const add_test_data = [
	AddTest{ 2, 3, 5 },
	AddTest{ 1024, 1024, 2048 },
	AddTest{ 1024, 1024, 2048 },
	AddTest{
		'84885164052257330097714121751630835360966663883732297726369399',
		'137347080577163115432025771710279131845700275212767467264610201',
		'222232244629420445529739893461909967206666939096499764990979600'
	},
]
// vfmt on

struct SubTest {
	minuend    TestInteger
	subtrahend TestInteger
	difference TestInteger
}

// vfmt off
const sub_test_data = [
	SubTest{ 2, 3, -1 },
	SubTest{ 3, 2, 1 },
	SubTest{ 1024, 1024, 0 },
	SubTest{ 1025, 1023, 2 },
	SubTest{ -37, -54, 17 },
	SubTest{
		'606649970971701574041811686113740270202550876483709086148210605390116266828180223053050209778348894403980564356645388786075884763988241017548715303094260467104311496756419021350006724017740287475611178032365989622403609173077177580917766063674998982266831659846883191228163839505500376638406380518500245205640396836251745520989267049355350124770273420388742964392659042390255365913227741421167939209094197448839427828553490702868654886044542868294392357965495270538217024561541188096726845410444367476070654975991208949317364651403789852688275944360458918380026777837704864009506175958100724200063354587117724445105352507371251655385818390017704579668464509118556289300149193216670709993199698192433115976551775179800369638443387559604023762303996929773104264181192752852969235821274398832731350067075624797873057893457025900696931888413170836093304160937994690818677414459443711940357617086640671088053946435282979649549418554954712352988774810660384276492179755146895039071579530616412414312102806763864081256558973934032350169887606110957605908487251732839382994454544873238510239304731396067785963044777530602823206256913249732064244398147271369308044244332002083201889676311272588373015273349596481282728910186314212094495644361558295140446865360550851657930415001417226985487444959716363633445225371706716914389021896551395496211883459788574558837045534063579702308219870597512062392856506558380417078205327200246935706335015817156527870338168318120000713685289299174193726065869808768486103389894925715767295',
		'7216848130098425850',
		'606649970971701574041811686113740270202550876483709086148210605390116266828180223053050209778348894403980564356645388786075884763988241017548715303094260467104311496756419021350006724017740287475611178032365989622403609173077177580917766063674998982266831659846883191228163839505500376638406380518500245205640396836251745520989267049355350124770273420388742964392659042390255365913227741421167939209094197448839427828553490702868654886044542868294392357965495270538217024561541188096726845410444367476070654975991208949317364651403789852688275944360458918380026777837704864009506175958100724200063354587117724445105352507371251655385818390017704579668464509118556289300149193216670709993199698192433115976551775179800369638443387559604023762303996929773104264181192752852969235821274398832731350067075624797873057893457025900696931888413170836093304160937994690818677414459443711940357617086640671088053946435282979649549418554954712352988774810660384276492179755146895039071579530616412414312102806763864081256558973934032350169887606110957605908487251732839382994454544873238510239304731396067785963044777530602823206256913249732064244398147271369308044244332002083201889676311272588373015273349596481282728910186314212094495644361558295140446865360550851657930415001417226985487444959716363633445225371706716914389021896551395496211883459788574558837045534063579702308219870597512062392856506558380417078205327200246935706335015817156527870338168318120000713685289299174193726065869808768486096173046795617341445',
	},
]
// vfmt on

struct MulTest {
	multiplicand TestInteger
	multiplier   TestInteger
	product      TestInteger
}

// vfmt off
const mul_test_data = [
	MulTest{ 2, 3, 6 },
	MulTest{ -869, 789, -685641 },
	MulTest{ -869, 789, -685641 },
	MulTest{
		'58347508324752098346789015701837509173586123875823769823749056132786590812379812508163208610983759827349812730',
		'14213098571932561236783245987342583245873465877179284713298718093758173895718957198751983759745983467856287759872365123980571298307512352359812753907',
		'829298887246357277468549979504958437413899799327432161171392486113084301523868147373815625368847440980180012682723545766706164916763514684200098693771786210459637448034580321756504306033681868059717237409850393775584813902875585559881251293651935822025836110',
	},
	MulTest{
		'157894047392519583862123936251422583282863434486161035821830453040785097509971311098714713467802515421581208662454854025309513312726555776113715381698473216141765400957534609297819356059233676661532235280302251223761244857785344705006637894902125836430726519102261679054112732204572631900384085596495981525783',
		'172750769107109861232750661741561572021207635860888814481781323903637921618217893620438648685692072759912890797284948206448457977080536627892119852493829355756035584640064060706092098984542716937323104336194056741403356304500304286336281585594296381344018535478383343176463402322692068381151973870461499748949',
		'27276318124492212465432029043652584179286098914879267159076000142724287743892530142734241126306064189405321580796475224876708675570413592358715948396724932685085972846389975012793388131866028332508690735727488135870026351955805826642739703000986587467280495743559787380135296551334471419857554510187338555406214731430906002559624746468831645485763335047438526317636794466414295301915951975626967409997005043887959717124157434636781107429998249363525957258509402650503217872035874896065023377356660153975719637017472701236397203173555796145677008784279961849944450443054513670798899182054747598702569678446196470652067',
	},
]
// vfmt on

struct DivModTest {
	dividend  TestInteger
	divisor   TestInteger
	quotient  TestInteger
	remainder TestInteger
}

// vfmt off
const div_mod_test_data = [
	DivModTest{13, 10, 1, 3},
	DivModTest{13, 9, 1, 4},
	DivModTest{7, 5, 1, 2},
	DivModTest{'2103180314840157', '1631403814113', 1289, '300798448500'},
	DivModTest{'21408410031413414147401', '3130541314113413', '6838564', '2900204736088469'}
	DivModTest{
		'13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096',
		'13407807926845237209807376456131917626043958556151178674833163543294276330515137663421134775482798690129946803802212663956180562088664022929883876655300863',
		1,
		'3097359889766648542073928501435407264441214702890397900427487699558409313380739522684104737560085054384273386897573320723282547016549772350783233',
	},
	DivModTest{
		'24388660549343689307666288436614463270948472382596516055139370678271628439810393042585576900667596857198513726355043536294702186342478937800831',
		'38733748171303628844838787580045178098452927239062690602450784467058896912531690007767956310348951731921168881868406259712',
		'629648864382619361826',
		'2724578611525334851445652767465274410979805962941953382558409365935061481311529445551691298696266856092833571769883246719',
	},
]
// vfmt on

struct DivTest {
	dividend TestInteger
	divisor  TestInteger
	quotient TestInteger
}

// vfmt off
const div_test_data = [
	DivTest{1234, 10, 123},
	DivTest{-1234, 10, -123},
	DivTest{1234, -10, -123},
	DivTest{-1234, -10, 123},
]
// vfmt on

enum Comparison {
	less    = -1
	equal   = 0
	greater = 1
}

struct ComparisonTest {
	lhs TestInteger
	rhs TestInteger
	cmp Comparison
}

// vfmt off
const comparison_test_data = [
	ComparisonTest{ -45, 35, .less },
	ComparisonTest{ -3, 13, .less },
	ComparisonTest{ u32(352395), u32(51830), .greater },
	ComparisonTest{ u32(52395), u32(52395), .equal },
	ComparisonTest{ '574720348957234098573049571938519023857709',
		'58745908123509182375091823601928385712908347298341752397182643294', .less },
]
// vfmt on

struct StrTest {
	value    TestInteger
	expected string
}

// vfmt off
const str_test_data = [
	StrTest{ 255, '255' },
	StrTest{ 127, '127' },
	StrTest{ 1024, '1024' },
	StrTest{ u64(4294967295), '4294967295' },
	StrTest{ u64(4398046511104), '4398046511104' },
	StrTest{ u64(-1), '18446744073709551615' },
	StrTest{ IntegerRadix{ 'e'.repeat(80), 16 }, '1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470' },
]
// vfmt on

struct BytesTest {
	value           TestInteger
	expected        []u8
	expected_signum int
}

// vfmt off
const bytes_test_data = [
	BytesTest{ u64(0x1337cafebabe), [u8(0x13), 0x37, 0xca, 0xfe, 0xba, 0xbe], 1 },
]
// vfmt on

struct HexTest {
	// `value` must be in hex, as we get a big.Integer with radix 16, then
	// check if the output of .hex() is the same as the original `value`.
	value string
}

// vfmt off
const hex_test_data = [
	HexTest{ '123' },
	HexTest{ 'a'.repeat(11) },
	HexTest{ 'b'.repeat(15) },
	HexTest{ 'c'.repeat(32) },
	HexTest{ 'd'.repeat(5185) },
	HexTest{ 'e'.repeat(5185) },
	HexTest{ 'cafe'.repeat(85) },
	HexTest{ '0' },
]
// vfmt on

struct PowTest {
	base     TestInteger
	exponent u32
	power    TestInteger
}

// vfmt off
const pow_test_data = [
	PowTest{ 2, 0, 1 },
	PowTest{ 2, 1, 2 },
	PowTest{ 2, 5, 32 },
	PowTest{ 2, 10, 1024 },
	PowTest{ 2, 30, 1073741824 },
	PowTest{ 2, 234, '27606985387162255149739023449108101809804435888681546220650096895197184' },
	PowTest{ 2, 524, '54918381281044877719855206392651145738155482401146443275155707673484345467181248416980477125291636439818370491131846864296975903997733150500592226328920457216' },
	PowTest{ 2, 291, '3978585891278293137243057985174566720803649206378781739523711815145275976100267004264448' },
	PowTest{ 2, 13051, '55267956948146482939580690892144167523198080345777838207973892310290788736821578114330455608899403999039109810492889769112675765371767842822434294904209432777633054910495035098236995390194172097907719098491142950680893121170415521376299250142695723393227633360829843822866024641522563385984082711736391348667978440753913719338262829073043347247299392139063557930790932287363651577574134082253690763995791163252208415844758727346329281304595424562991322680879863770722706799961809876056054813814521080566806686543718660613598426151803438498674178140121315919034857314115503781267190616565875833106631659285123886667080327050174176636018357664222092316439777145540128647880814336995055642164733977751410563822190384321689041057299634727749812974144023207025653904438780374867988942510987313912358915642077631661267839921263001062107729173356916478717702317027506423498272334490092958377684733699662429071107215486273972618250534115869723283498114137145525373906475082833289104669492127120264073453925068828669125794548775264089233042803009595366761575381131720150020416662597433270991992738498853512916968911947303026796659580500368762437079002069661349769285545784372450396095376496629526120146056228969588758257289220372378463001533895184835727768908852564233327092981929411751106985223047814350329661006786989543912993534105514927316542621884655977620130711246926843567109456472355504038497809114457931293164130666363146093971664611970414478485192994816264742799354431903294883107927606299200191395563965045258006700385533997784569469661002395464091760511030079955508794507716517333922113296290807049250301483689712907877303498967096232669748824940257740874215641972642675515304831959934847559373172772025875939941676740275689817054162918846662455567085919928556650978239087803483530882408322645489433079587537294621171822004369279659604911446107164265409771411479042191057214450986176256578104398112366947709868794481776078240442638184240046590399307658325666682824402555546582169125084299171911366515003536406493880562467793738217233554231178610461637407108387422104493358650812565981939865004194724312308819851360505832016659131404286536166604042995985362843053197530692653760538388205506347898194204185268035169011980847365832662902833167362084522724032944503999606348026648228492541892525660287991575613769500299950657228084617208982541726107251394954812792421965613075539830944521759805581201863306109230481535735866977702920938163637191044218917218763676996478413703527738808285760128085267402333007136279283870165611587883351595579551530437487879301447035074519559776804970191650066388050122761542282202772884141794482588913487773583646082244648646898935682167457258599202486566745556266587599528210145725708324833027469913664602498185234663551855895508091756784116786760256088764788750159984191050146275837122761351322740020869396023016337700936855961260331330031678574208143275974341369638055331776401934895373348591440122433244331646746067969385005236926872015898048323445768868147713435873976237988151319143506783129013204847868879150394120372376677277312144782189330256123263292237715700439809503932239415509520929779856708018666527067600827221597869399175438321388641981165828368557419771140117333070030872143102607788421073131616871657178020327435898081376870580959579623464843234800465871796556467456341474610533391530095305196315887535933316836459037786625667180354374811843995688474490608169113180680143676925870456310071925085465713774179009107449991929491343540484244181606094673172473788660554675839157004904589906389801922612312350223513086754383162398737177774230411158150377305076748192396441454734336654918968445280484743879759859090769539002965788922965135895758979301734303194298909826556775859142046683115828058122817136236184480127270446495870009273426474586242661860904296452193946364822434632085953525238418258226591281397400949183681177585810194950485799542968112294068369136156497511211228725248' },
	PowTest{ 324, 315, '66325146064916587705822805477951823674769212922003325230500180789514487101799702287247301347816140714887582527826252837635296749781071351621748491469338347097923896026211183517655658952346069454893422558286798338709431368762851475568899541999504754550056265493269010870696623999709399529395247064542825851568385196637089440522882877102429945439977107582295420418108331098961838419917230847980056560488541780255425015021238743932289115066701337398107639567748102191005710201353615093246958907555634902309636451244444952203735074916066229982498598205421944122042066749035283837586883383420374291325389757869347147357807188516650352693616763867685354382631931356465247637321960345782811272139101785279798666504361229957479336436466489780129445016691164329417001378480690804715301830926348058624' },
]
// vfmt on

struct ModPowTest {
	base     TestInteger
	exponent u32
	modulus  TestInteger
	power    TestInteger
}

// vfmt off
const mod_pow_test_data = [
	ModPowTest{ 324, 315, 632, 512 },
	ModPowTest{ 65, 17, 3233, 2790 },
	ModPowTest{ 2790, 413, 3233, 65 },
]
// vfmt on

struct BigModPowTest {
	base     TestInteger
	exponent TestInteger
	modulus  TestInteger
	power    TestInteger
}

// vfmt off
const big_mod_pow_test_data = [
	BigModPowTest{ 23, 35, 4205, 552 }, // passed to mod_pow
	BigModPowTest{
		'5155371529688',
		'2791323022555160232601405723625177570767523893639864538140315412',
		'108959927459825236754563833',
		'26860526814751021488886966'
	},
	BigModPowTest{ // odd modulus
		'352374589237450928347609812740958719304509123759871239856523745782375908723095729758275893947985713250128357912349123412939358823582385385198351236046127834612374812370491327508137250913279785120956123875610235871239857198203659081236598012735981327096129836712397538257324587324658973246586212305713209851290386517823650983',
		'1435987139846732486843289483294829493285238539385476982375980650172394182703846128975612083751298035612365812378908579834576456739458768754674674758467957697513267415734673649713598712634987126359872685236563875',
		'235091287508475298347528357901372598012305123512513252139683276908769284375274983274732895287478365817326581273509823651087861235876123563725193512827341395182375612384723587131912725187352562171235812387132578235385239913578132684794719844871326571632561325617256983275689175687132487134894717895941563769587165871727358791326578126587261589732165781623578921653625871256781326587132658716237561235612783569812358791263985688921356634756374561398756298576128463875627',
		'169366899917096971139137080261692248176119100321982279129907315595573960531869689311776921667033660828498254917762241531493510090131310371752611041591194730984721766392649610729748330695093443476931010397820703902902374473806912248240019857777647140275706106146415293419050004306619925031697942412075960435433660980502714238024766862370671258678169869169308956145487178274009104771385352022837969419172440632584754050020898915411115587200113298960058827089497449758704',
	},
	BigModPowTest{
		'65963856903485603485069234860923480925798172358172309571298347102836578126398512837409812375712637895612379856123981237510298356120347129803401236571623978561287347129837563453245983475983457439573985398456987356198236481727340813259182735619827356189723658763578623842342348791856178236598273498213492641265929375983589324658972364587869871263587612387568767345727346273647235738786247865198746132879461234613284823467',
		'83272598179813257612837472873648237598405840629839475914921364871236813258573298457912734897123942134273981',
		'5839857294085157278513295172390857198203571235213752396329429342385198326512374982374982394818327590816263059612397512983479812378951235123571239857908674389573485791835127834617823648791263587321501273958734987520983475818586128374612837589734985729073459081628387561982736547958729875923652356138756183576',
		'1473628912328925119498300555459646092229919432628304743927027840523966896206312866878166443381090567655363186687840805386391819767096022910509388879763843632740700147538366282034781700814905320520871339523971198789410048087451530293781098724024347130773477767285944506944257502250353223975013799044669645147',
	},
	BigModPowTest{
		'113242346235324852305823906832570234458325884382848882352385183957109823579028553482572308468203945823946234096234056990567506978059607985823049523458273598234750329857',
		'8' + '0'.repeat(304),
		'538232475982347092375235252304678234670293486732980467394932151374571938467459586739087429813752353459823475982375983259823495510986571430996238476587342658172851235761398456398765813459801238751782351252542389712789568712342749837482748292340120120350125012352859821657823501275012365123985761983576123561273561257632958127659327160124712350123571029356123512507123598321797',
		'192406008638594029999856291470760872362212741932343638540342189789173751505083836385843995643278520411152869906156437055678588013127671381619800101872065097151880787029771615331116606406595932983669815757450573569058422413443800383057849205750667577751024330669200452034408252515262931175662782786664172617789499914619948520343146045783354828154425927051367644302499369000252',
	},
	BigModPowTest{ // even modulus
		'235823052395732458137502345873250',
		'9247324572804102889565555777311914057954687482673431192869682151395651003606366864848904841770165182604035932529621174486515688424932060959148379649412557',
		'2176617445861743577319100889181907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288818',
		'442997670902441289693502035236411859724060788065895656400579052337406446773758010436855122951259016429941998670112046510440662262181776245572088822784133641469508278250883828352296420961829921453124482851450238472747959697964344812370952273954391788494014188845640003786259215718720844436373044245757220429613176387487778233231542432645946351180509817097194411903946161272789482180797502735809791321387092632573236334305080001390892058454801700346356574718281589228939135660037060121182304974907075601193595994210348704718150369101358547875357900995290428579291079564736470284970163062216850888677989344123412'
	},
	BigModPowTest{
		'5155342534532472457235424353715296885321784212092346234575235718572938457284759829789708703754094283682345782309123940810923850923745723498672398459023845028309402347698798128209480234598716824503042159',
		'86088201503667039039649712985497870124991029230637396538140315482910296196688861780721773488400937149055855947456947824680518799310618149812585881185273945728945238450238465982768132749812308932579342985729345798328459832591982374981237590923882894732852358383128938192888912389148132163716923461512177401900495710673973552425181964932538140315421936569702910921834517138450159290932430254268769414',
		'274591906405224388599271723580726658164960647214841029141336415219736447718088739565548373811507267740223510760319632557286907774120057322163752385728943578236484780326508234652347012734098132747829056238742677853836742172732148487951998402235107603196866575250296113164460906849287604912497386435248807205228608',
		'191409711082233170599206818399072245570776992384487887400529042633340865718475272702984694979757086140971619519330041885966232600204343618098862571914303534164078578922423233100444505069197147657365390879382972696958855409999020160689093198793125749417593529720182526801136320146027275209623807723638007207345057',
	},
	BigModPowTest{ // power of 2 modulus
		'853475230582304956623462734587234857',
		'583752847502934572695871320598123057810835791832765601237490813250912360591237591247132543095792347598327957137481023974819237887128577123758127912735720912735708362178561027835610235610256231201472309873219800321951',
		'31195165372897259196222538898096203590151924108450147950531565441852619837316692843188389598728651769482088968838700984268947453885587967878549286444999755742573423371025356539077075265986419171772426279084559025861175301940492273427120221755816136975739916983004778387946699939545354293487098252428954036286183995782377175227121587657233553706589448547148066273280603243167958729707736664649187444136702017299877489729451997277875868782399735511520086969969766278182145454186690598629675562422923132555707758646587702550600894625696538109646366308973392363200122154242784576162149305816215109893613161331026672647000825615987247035266514313689413563779184515427920269935280569035788081552413007563772309295149800172031645681720569680154349893907395864528243629654386620034655445226295834594630792819545156798270599481573436039129275439653984521135652249263653985326577886990615665734998585216581730937090703518997669223802429711292740491797911117308280939507973715877108492303860661291987529284719391551256912380499409630332506454532263266457209921483705507359152839264852808182519011100934922492651373859423833024010283468753147686188675294998119637462200763443029190704825719342806119404339670408160210011918981038977425180213726646978883378058838510330816291941879581568740273684084511318422175006728346276489384220596694727036836687670632486602655240593463885077059375085482211864761344849868123074687509143827139683659102930877963676911995751113159944160296419825178911962487549670296207457410515598040046860567719116506974858703739531721991704589155513182996455827177472',
		'20080128881481836026329919458482291336427826856257996902940216961274769492432457342329585911526680598599706497937330675336231322550089070961997878231160797014417182095610964306078005177657705895303701167263611016504813896879268670353690809857529479048652197026092060293580162938995900771157020773980149060390600886988165389386800313399935681668574561849644795370126640258861886083152431680309682414362667647047408205246634496700184520404072535696087062725321498879508195864728319264953502905783599235208616928912295007582161378604427602444176983900856363236858555001132922259491209136145654862306503211818341793436738181575298025051779368026867674815572620689517665845691581156037531328681002192885908572782500210185856071870996928958700199226442991667291661898788475465881534872952976564959204125435567408766407069080824764398534937188392006557445009391997597405999602710062336419706104598663078108578494090878260551511868925451048198114479011315931259263368212342695453474607592164864833976245439378166385649596428707473508482940426102862136582304433403471613967149235623865180698593404828164563801111816849132293956996556247415009648932020221786934682827799748481777622180801590275002765776928805756187584952776044614940158088391767782343033313716059721086499569092199761880797791493764006487948292742803823944731896556184944138299575510183812680359579022267096795084742403276965131310267236257021618824374001979653996305168583849734973984410291953747451176667816227487401602863864660642232048020325795772190251487050535547981550465960228478297407995702324941118877701015321',
	},
]
// vfmt on

struct GCDTest {
	a        TestInteger
	b        TestInteger
	expected TestInteger
}

// vfmt off
const gcd_test_data = [
	GCDTest{ 0, 0, 0 },
	GCDTest{ 10, 0, 10 },
	GCDTest{ 10, 0, 10 },
	GCDTest{ 1, 1, 1 },
	GCDTest{ 0, 1, 1 },
	GCDTest{ 0, -18, 18 },
	GCDTest{ 51, 22, 1 },
	GCDTest{ 98, 56, 14 },
	GCDTest{ '67116917544110', '60943431483592', '6299482' },
]
// vfmt on

struct FactorialTest {
	base     TestInteger
	expected TestInteger
}

// vfmt off
const factorial_test_data = [
	FactorialTest{ 5, 120 },
	FactorialTest{ 100, '93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000' },
]
// vfmt on

// for left_shift and right_shift
struct ShiftTest {
	base     TestInteger
	amount   u32
	expected TestInteger
}

// vfmt off
const left_shift_test_data = [
	ShiftTest{ 45, 2, 45 * 4 },
	ShiftTest{ 45, 3, 45 * 8 },
	ShiftTest{ 45, 4, 45 * 16 },
	ShiftTest{ 45, 5, 45 * 32 },
	ShiftTest{ u64(0xabcedabcde), 20, u64(0xabcedabcde00000) },
	ShiftTest{ [u8(1), 1, 1], 56, [u8(1), 1, 1, 0, 0, 0, 0, 0, 0, 0] },
]

const right_shift_test_data = [
	ShiftTest{ 45, 3, 5 },
	ShiftTest{ 0x13374956, 16, 0x1337 },
	ShiftTest{ [u8(1), 1, 1, 0, 0, 0, 0, 0, 0, 0], 56, [u8(1), 1, 1] },
]
// vfmt on

struct ISqrtTest {
	base     TestInteger
	expected TestInteger
}

// vfmt off
const isqrt_test_data = [
	ISqrtTest{ '32653970479009599018236258903443284683789766179879955366041757982300361890625826551918485225', '5714365273502351023665817936982376512387569235' },
	ISqrtTest{ '1234567901234567901234567901234567901234567901234567901234567901234567901234567901234567901234567654320987654320987654320987654320987654320987654320987654320987654320987654320987654320987654321', '1'.repeat(97) },
]
// vfmt on

struct BitLenTest {
	value    TestInteger
	expected u32
}

// vfmt off
const bit_len_test_data = [
	BitLenTest{ big.zero_int, 0 },
	BitLenTest{ big.one_int, 1 },
	BitLenTest{ u32(0xffffffff), 32 },
	BitLenTest{ big.one_int.left_shift(1239), 1240 },
	BitLenTest{ '4338476092346017364013796407961305761039463198075691378460917856', 212 },
]
// vfmt on

struct ModInverseTest {
	value   string
	modulus string
}

// vfmt off
const mod_inverse_test_data = [
	ModInverseTest{'1234567', '458948883992'},
	ModInverseTest{
		'239487239847',
		'2410312426921032588552076022197566074856950548502459942654116941958108831682612228890093858261341614673227141477904012196503648957050582631942730706805009223062734745341073406696246014589361659774041027169249453200378729434170325843778659198143763193776859869524088940195577346119843545301547043747207749969763750084308926339295559968882457872412993810129130294592999947926365264059284647209730384947211681434464714438488520940127459844288859336526896320919633919',
	},
]
// vfmt on

fn test_integer_from() {
	for test_data in [
		// add integer_from_..._test_data to this array
		integer_from_int_test_data,
		integer_from_u64_test_data,
		integer_from_bytes_test_data,
		integer_from_string_test_data,
		integer_from_radix_test_data,
	] {
		for t in test_data {
			assert t.expected.trim_string_left('0x') == if t.expected.starts_with('0x') {
				t.value.parse().hex()
			} else {
				t.value.parse().str()
			}
		}
	}
}

fn test_bytes() {
	for t in bytes_test_data {
		r, s := t.value.parse().bytes()
		assert r == t.expected
		assert s == t.expected_signum
	}

	mut bytes := []u8{cap: 1024}
	for i in 0 .. bytes.cap {
		bytes << u8(i | 1)
	}
	result, sign := big.integer_from_bytes(bytes).bytes()
	assert result == bytes
	assert sign == 1
}

fn test_is_power_of_2() {
	for t in is_power_of_2_test_data {
		assert t.value.parse().is_power_of_2() == t.expected
	}
}

fn test_is_odd() {
	for t in is_odd_test_data {
		assert t.value.parse().is_odd() == t.expected
	}
}

fn test_addition() {
	for t in add_test_data {
		assert t.augend.parse() + t.addend.parse() == t.sum.parse()
	}
}

fn test_subtraction() {
	for t in sub_test_data {
		assert t.minuend.parse() - t.subtrahend.parse() == t.difference.parse()
	}
}

fn test_mul() {
	for t in mul_test_data {
		assert t.multiplicand.parse() * t.multiplier.parse() == t.product.parse()
	}

	for t in div_mod_test_data {
		assert t.divisor.parse() * t.quotient.parse() == t.dividend.parse() - t.remainder.parse()
	}
}

fn test_div() {
	for t in div_mod_test_data {
		assert t.dividend.parse() / t.divisor.parse() == t.quotient.parse()
	}
	for t in div_test_data {
		assert t.dividend.parse() / t.divisor.parse() == t.quotient.parse()
	}
}

fn test_mod() {
	for t in div_mod_test_data {
		assert t.dividend.parse() % t.divisor.parse() == t.remainder.parse()
	}
}

fn test_div_mod() {
	for t in div_mod_test_data {
		a := t.dividend.parse()
		b := t.divisor.parse()
		eq := t.quotient.parse()
		er := t.remainder.parse()

		q, r := a.div_mod(b)
		assert q == eq
		assert r == er
	}

	// an extra test for checked division by zero
	if _, _ := div_mod_test_data[0].dividend.parse().div_mod_checked(TestInteger(0).parse()) {
		assert false, 'Division by 0 should return an error'
	}
}

fn test_comparison() {
	for t in comparison_test_data {
		a := t.lhs.parse()
		b := t.rhs.parse()

		assert a == a
		assert a <= a
		assert a >= a

		assert b == b
		assert b <= b
		assert b >= b

		lt := t.cmp == Comparison.less
		gt := t.cmp == Comparison.greater
		eq := t.cmp == Comparison.equal

		assert (a < b) == lt
		assert (a > b) == gt
		assert (a <= b) == !gt
		assert (a >= b) == !lt
		assert (a == b) == eq
		assert (a != b) == !eq
	}
}

fn test_conversion() {
	ten := big.integer_from_int(10)

	mut n := big.integer_from_u64(u64(-1))

	mut digits := []rune{}
	for n.signum != 0 {
		quot, rem := n.div_mod(ten)
		digits << rune(rem.int()) + `0`
		n = quot
	}

	assert digits.reverse().string() == '18446744073709551615'
}

fn test_from_and_to_hex() {
	for t in hex_test_data {
		assert (big.integer_from_radix(t.value, 16) or { panic('Cannot read hexadecimal') }.hex()) == t.value
	}
}

fn test_str() {
	for t in str_test_data {
		assert t.value.parse().str() == t.expected
	}
}

fn test_pow() {
	for t in pow_test_data {
		assert t.base.parse().pow(t.exponent) == t.power.parse()
	}
}

fn test_mod_pow() {
	for t in mod_pow_test_data {
		assert t.base.parse().mod_pow(t.exponent, t.modulus.parse()) == t.power.parse()
	}
}

fn test_big_mod_pow() {
	for t in big_mod_pow_test_data {
		a := t.base.parse()
		e := t.exponent.parse()
		m := t.modulus.parse()
		r := t.power.parse()
		assert a.big_mod_pow(e, m) or { panic(err) } == r
	}
}

fn test_gcd_vs_gcd_binary_vs_gcd_euclid() {
	for t in gcd_test_data {
		a := t.a.parse()
		b := t.b.parse()
		expected := t.expected.parse()
		assert a.gcd(b) == expected
		assert a.gcd_binary(b) == expected
		assert a.gcd_euclid(b) == expected
	}
}

fn test_factorial() {
	for t in factorial_test_data {
		a := t.base.parse()
		r := t.expected.parse()
		assert a.factorial() == r
	}
}

fn test_inc_and_dec() {
	mut a := big.integer_from_int(2)
	mut b := big.integer_from_int(3)
	mut c := big.integer_from_int(4)

	a.inc()
	c.dec()
	assert a == b
	assert b == c
}

fn test_left_shift() {
	for t in left_shift_test_data {
		assert t.base.parse().left_shift(t.amount) == t.expected.parse()
	}
}

fn test_right_shift() {
	for t in right_shift_test_data {
		assert t.base.parse().right_shift(t.amount) == t.expected.parse()
	}
}

fn test_bit_len() ? {
	for t in bit_len_test_data {
		assert t.value.parse().bit_len() == t.expected
	}
}

fn test_mod_inverse() ? {
	for t in mod_inverse_test_data {
		a := big.integer_from_string(t.value) or { panic('Could not read decimal') }
		m := big.integer_from_string(t.modulus) or { panic('Could not read decimal') }
		r := a.mod_inverse(m) or { panic('No multiplicative inverse in ring Z/mZ') }
		assert (r * a) % m == big.one_int
	}
}

fn test_isqrt() {
	for i in 0 .. 1000 {
		a := big.integer_from_int(i)
		b := big.integer_from_int(i * i)
		assert b.isqrt() == a
	}
	values := [
		TestInteger(314),
		'213149',
		'2198614',
		'318014',
		'1000000000',
		'1000131039410',
		'2148170394871039847019843349714981',
	]
	for value in values {
		a := value.parse()
		b := a * a
		assert b.isqrt() == a
	}

	for t in isqrt_test_data {
		assert t.base.parse().isqrt() == t.expected.parse()
	}
}

fn test_bitwise_ops() {
	a := big.integer_from_int(1).left_shift(512)
	b := a - big.one_int
	assert a.bitwise_and(b) == big.zero_int
	assert b.bitwise_xor(b) == big.zero_int
	assert b.bitwise_or(b) == b
	assert b.bitwise_and(b) == b
	assert b.bitwise_not() == big.zero_int
}

fn test_get_bit() {
	x := big.integer_from_int(42)
	assert x.get_bit(0) == false
	assert x.get_bit(1) == true
	assert x.get_bit(2) == false
	assert x.get_bit(3) == true
	assert x.get_bit(4) == false
	assert x.get_bit(5) == true
	assert x.get_bit(10) == false

	values := ['1001001001001010010010100100100101011101001001010',
		'1111111111111111111111111111111111111111111111111',
		'0000000000000000000000000000000000000000010000', '1110010', '0001001']
	for value in values {
		a := big.integer_from_radix(value, 2) or { panic('Could not read binary') }
		bits := []bool{len: value.len, init: value[value.len - 1 - index] == `1`}
		for i in 0 .. value.len {
			assert a.get_bit(i) == bits[i]
		}
	}
}

fn test_set_bit() {
	mut a := big.integer_from_int(32)
	a.set_bit(1, true)
	assert a.int() == 34
	a.set_bit(1, false)
	a.set_bit(3, true)
	assert a.int() == 40
	a.set_bit(50, true)
	assert a == big.one_int.left_shift(50) + big.integer_from_int(40)
	b := a
	a.set_bit(100, false)
	assert a == b
}
